#pragma once

#include "pch.h"
#include "Method.h"
#include "Field.h"
#include <unordered_map>


namespace mono
{
    struct Clazz
    {
        std::string m_Name;
        
        std::string m_Namespace;

        MonoClass *m_Class;

        std::unordered_map<std::string, std::shared_ptr<Clazz>> m_InnerClasses; 

        Clazz(MonoClass *clazz) : m_Class(clazz)
        {
            m_Name = mono_class_get_name(m_Class);
            m_Namespace = mono_class_get_namespace(m_Class);
            LoadInnerClasses();
        }

        bool HasInnerClass(const std::string &name)
        {
            return m_InnerClasses.find(name) != m_InnerClasses.end();
        }

        std::shared_ptr<Clazz> GetInnerClass(const std::string &name)
        {
            return m_InnerClasses.at(name); 
        }
        std::shared_ptr<Field> GetField(const std::string& name)
        {
            return std::make_shared<Field>(m_Class, name.c_str());
        }
        std::shared_ptr<Method> GetMethod(const std::string &name, int numParams)
        {
            auto monoMethod = mono_class_get_method_from_name(m_Class, name.c_str(), numParams);
            if (!monoMethod)
            {
                ztclog::info("Failed to load mono method: %s" , name.c_str());
                return nullptr;
            }
            return std::make_shared<Method>(monoMethod);
        }
        std::shared_ptr<struct Object> NewInstance(bool runtimeInvoke = true);

        MonoObject* NewInstanceObject();

        void LoadInnerClasses() 
        {
            void *iter = nullptr;
            MonoClass *nestedClass;

            while ((nestedClass = mono_class_get_nested_types(m_Class, &iter)) != nullptr)
            {
                std::string nestedClassName = mono_class_get_name(nestedClass);
                m_InnerClasses[nestedClassName] = std::make_shared<Clazz>(nestedClass);
            }
        }
    };
};
